﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Pathfinding;
using Pathfinding.RVO;

/** AI for following paths.
 * This AI is the default movement script which comes with the A* Pathfinding Project.
 * It is in no way required by the rest of the system, so feel free to write your own. But I hope this script will make it easier
 * to set up movement for the characters in your game. This script is not written for high performance, so I do not recommend using it for large groups of units.
 * \n
 * \n
 * This script will try to follow a target transform, in regular intervals, the path to that target will be recalculated.
 * It will on FixedUpdate try to move towards the next point in the path.
 * However it will only move in the forward direction, but it will rotate around it's Y-axis
 * to make it reach the target.
 *
 * \section variables Quick overview of the variables
 * In the inspector in Unity, you will see a bunch of variables. You can view detailed information further down, but here's a quick overview.\n
 * The #repathRate determines how often it will search for new paths, if you have fast moving targets, you might want to set it to a lower value.\n
 * The #target variable is where the AI will try to move, it can be a point on the ground where the player has clicked in an RTS for example.
 * Or it can be the player object in a zombie game.\n
 * The speed is self-explanatory, so is turningSpeed, however #slowdownDistance might require some explanation.
 * It is the approximate distance from the target where the AI will start to slow down. Note that this doesn't only affect the end point of the path
 * but also any intermediate points, so be sure to set #forwardLook and #pickNextWaypointDist to a higher value than this.\n
 * #pickNextWaypointDist is simply determines within what range it will switch to target the next waypoint in the path.\n
 * #forwardLook will try to calculate an interpolated target point on the current segment in the path so that it has a distance of #forwardLook from the AI\n
 * Below is an image illustrating several variables as well as some internal ones, but which are relevant for understanding how it works.
 * Note that the #forwardLook range will not match up exactly with the target point practically, even though that's the goal.
 * \shadowimage{aipath_variables.png}
 * This script has many movement fallbacks.
 * If it finds a NavmeshController, it will use that, otherwise it will look for a character controller, then for a rigidbody and if it hasn't been able to find any
 * it will use Transform.Translate which is guaranteed to always work.
 */
[RequireComponent(typeof(Seeker))]
[AddComponentMenu("Pathfinding/AI/AIPath (3D)")]
[HelpURL("http://arongranberg.com/astar/docs/class_a_i_path.php")]
public class MyPath : MonoBehaviour {

	public float repathRate = 0.5F;
	public List<Vector3> pathv3;
	public Transform target;
	public bool canSearch = true;
	public bool canMove = true;
	public float speed = 3;
	public float turningSpeed = 5;
	public float endReachedDistance = 0.2F;
	protected Seeker seeker;

	protected Path path;

	protected bool canSearchAgain = true;
	protected Transform _cachedTransform;
	/** Initializes reference variables.
	 * If you override this function you should in most cases call base.Awake () at the start of it.
	 * */
	protected virtual void Awake () {
		_cachedTransform = transform;
		seeker = GetComponent<Seeker>();

	}
	public virtual void OnTargetReached () {

		//End of path has been reached
		//If you want custom logic for when the AI has reached it's destination
		//add it here
		//You can also create a new script which inherits from this one
		//and override the function in that script
	}
	/** Starts searching for paths.
	 * If you override this function you should in most cases call base.Start () at the start of it.
	 * \see OnEnable
	 * \see RepeatTrySearchPath
	 */
	protected virtual void Start () {
		OnEnable();

		TrySearchPath();
	}

	protected IEnumerator RepeatTrySearchPath () {
		while (true) {
			float v = TrySearchPath();
			yield return new WaitForSeconds(v);
		}
	}
	protected virtual void OnEnable () {
		seeker.pathCallback += OnPathComplete;
	}

	public void OnDisable () {
		seeker.pathCallback -= OnPathComplete;
	}


	/** Tries to search for a path.
	 * Will search for a new path if there was a sufficient time since the last repath and both
	 * #canSearchAgain and #canSearch are true and there is a target.
	 *
	 * \returns The time to wait until calling this function again (based on #repathRate)
	 */
	public float TrySearchPath () {
			SearchPath();
			return repathRate;
	}

	/** Requests a path to the target */
	public virtual void SearchPath () {
		if (target == null) throw new System.InvalidOperationException("Target is null");
	
		//This is where we should search to
		Vector3 targetPosition = target.position;

		canSearchAgain = false;

		//Alternative way of requesting the path
		//ABPath p = ABPath.Construct (GetFeetPosition(),targetPoint,null);
		//seeker.StartPath (p);

		//We should search from the current position
		seeker.StartPath(GetFeetPosition(), targetPosition);
	}


	/** Called when a requested path has finished calculation.
	 * A path is first requested by #SearchPath, it is then calculated, probably in the same or the next frame.
	 * Finally it is returned to the seeker which forwards it to this function.\n
	 */
	public virtual void OnPathComplete (Path _p) {
		ABPath p = _p as ABPath;
		path = p;
		pathv3 = path.vectorPath;
		canSearchAgain = true;
		pathpointID = 0;
	}
	public int pathpointID;

	bool _canSearchPath;
	float _canSearchPathTime;
	bool canSearchPath{
		set{ 
			if (value != _canSearchPath) {
				_canSearchPath = value;
				if(value == true)
					_canSearchPathTime = Time.time;
			}
		}
		get{ 
			return _canSearchPath && Time.time - _canSearchPathTime > repathRate;
		}
	}
	public void Update(){
		if (!canMove)
			return;
		if (canSearch && canSearchPath) {
			canSearchPath = false;
			TrySearchPath ();
		}
			
		if (pathpointID >= pathv3.Count) {
			OnTargetReached ();
			return;
		}
		if ((pathv3 [pathv3.Count - 1] - pathv3 [pathv3.Count - 2]).magnitude < endReachedDistance) {
			OnTargetReached ();
			return;
		}
			
		float movedis = Time.deltaTime * speed;
		if (Vector3.Distance (transform.position, pathv3 [pathpointID]) < movedis) {
			pathpointID++;
			canSearchPath = true;
			if (pathpointID >= pathv3.Count)
				return;
			
		}
		LookAtPoint ();
		MoveTo (movedis);

	}
	public void LookAtPoint(){
//		transform.LookAt (pathv3 [pathpointID]);
		Quaternion rotate = Quaternion.LookRotation(pathv3 [pathpointID] - _cachedTransform.position);  

		_cachedTransform.rotation = Quaternion.Slerp(_cachedTransform.rotation, rotate, Time.deltaTime * turningSpeed);
	}
	public void MoveTo(float movedis){
		Vector3 nextpoint = pathv3 [pathpointID];
//		transform.Translate ((nextpoint - transform.position).normalized * movedis);
		_cachedTransform.position += (nextpoint - transform.position).normalized * movedis;
	}

	public virtual Vector3 GetFeetPosition () {
		return _cachedTransform.position;
	}
}
